package net.sevear.chaos.recommend
{
	import com.adobe.serialization.json.JSON;
	import com.douban.auth.DBAuthenticate;
	import com.douban.utils.Format;
	
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;
	import flash.net.URLVariables;
	import flash.utils.Timer;
	
	import mx.collections.ArrayCollection;
	
	import net.sevear.chaos.douban.DBShuoManager;
	import net.sevear.chaos.douban.NeedUpdatedPlayList;
	import net.sevear.chaos.douban.SendMailTool;
	import net.sevear.chaos.download.Predef;
	import net.sevear.chaos.event.HasNewChaosEvent;
	import net.sevear.chaos.event.RecommendEvent;
	import net.sevear.chaos.event.authenticate.LoginEvent;
	import net.sevear.chaos.utils.Logger;
	import net.sevear.chaos.view.ChaosLogPanel;
	import net.sevear.chaos.vo.ChaosEvtInfo;
	import net.sevear.chaos.vo.RecommendInfo;
	import net.sevear.chaos.vo.TrackInfo;
	import net.sevear.chaos.vo.UserInfo;
	
	import org.osmf.events.TimeEvent;
	
	public class RecommendManager extends EventDispatcher
	{
		private static const LOGIN_URL:String = "/login";
		public static const RECOMMEND_URL:String = "/r/";
		private static const TRACK_INFO_URL:String = "/t";
		
		private static const REGISTER_USER:int = 0;
		private static const GET_CONTANCT_LIST:int = 2;
		private static const SEND_RECOMMENDATION:int = 1;
		private static const RESPONSE_RECOMMENDATION:int = 3;
		private static const GET_EVENT_LIST:int = 4;
		private static const SEND_RECOMMENDATIONS:int = 5;
		private static const GET_CONTACTS_RECENT_LIKE:int = 6;
		private static const GET_RECOMMEND_STAT:int = 7;
		
		private static const REGISTER_SUCCRESSULLY:int = 0;
		
		private var loader:URLLoader;
		private var getListLoader:URLLoader;
		
		private var op:int;
		private var remainList:ArrayCollection;
		
		private var listTimer:Timer;
		private var isRegistered:Boolean = false;
		private var isSendingRequest:Boolean = false;
		
		private static var _instance:RecommendManager;
		
		private var sentReItems:ArrayCollection;
		private var sentReIds:ArrayCollection;
		
		private var _curRId:int;
		private var _reOp:String;
		
		private var _responeseIDs:Array = [];
		private var _responsesOps:Array = [];
		
		private var _curRecommendTrack:TrackInfo;
		private var _destUID:int;
		
		private var _sid:int;
		
		public var authenticater:ChaosAuthenticater;
		public var evtManager:ChaosEvtManager;
		
		private var mailSender:SendMailTool;
		
		public static function getInstance():RecommendManager{
			if(_instance==null){
				_instance = new RecommendManager();
			}
			return _instance;
		}
		
		public function RecommendManager(target:IEventDispatcher=null)
		{
			super(target);
			loader = new URLLoader();
			loader.addEventListener(Event.COMPLETE,onComplete);
			loader.addEventListener(IOErrorEvent.IO_ERROR,onIOError);
			
			remainList = new ArrayCollection();
			sentReItems = new ArrayCollection();
			sentReIds = new ArrayCollection();
			mailSender = new SendMailTool();
			
			authenticater = new ChaosAuthenticater();
			authenticater.addEventListener(LoginEvent.REGIST_SUCCESSFUL,onLogin);
			evtManager = new ChaosEvtManager();
			evtManager.addEventListener(RecommendEvent.GET_NEW_RECOMMENDLIST,onGetNewRes);
			evtManager.addEventListener(HasNewChaosEvent.HAS_NEW_EVENT,onHasNewEvents);
			
			DBShuoManager.getInstance().addEventListener(RecommendEvent.GET_NEW_RECOMMENDLIST,onGetNewRes);
		}
		
		public function getRecomendItems(sid:int):Array{
			var reItems:Array = [];
			for each(var reItem:RecommendInfo in this.remainList){
				if(reItem.trackInfo.sid == sid){
					reItems.push(reItem);
				}
			}
			return reItems;
		}
		
		public function getRecommendStat(destID:int):void{
			if(canSend()){
				var vars:URLVariables = new URLVariables();
				vars.uid= DBAuthenticate.getInstance().dbUID;
				vars.op = "stat";
				var rst:URLRequest = new URLRequest(encodeURI(Predef.CHAOS_HOST+RECOMMEND_URL));
				rst.data = vars;
				op = GET_RECOMMEND_STAT;
				loader.load(rst);
			}
		}
		
		public function getEvents(type:int):void{
			var vars:URLVariables = new URLVariables();
			vars.uid= DBAuthenticate.getInstance().dbUID;
			vars.type = type;
			var rst:URLRequest = new URLRequest(encodeURI(Predef.CHAOS_HOST+ChaosEvtManager.EVENT_URL));
			rst.data = vars;
			rst.method = URLRequestMethod.GET;
			op = GET_EVENT_LIST;
			isSendingRequest = true;
			loader.load(rst);
		}
		
		public function recommend(track:TrackInfo,destUID:int):void{
			if(canSend()){
				_curRecommendTrack = track;
				_destUID = destUID;
				var rst:URLRequest = new URLRequest(encodeURI(Predef.CHAOS_HOST+RECOMMEND_URL));
				var vars:URLVariables = new URLVariables();
				var recommedInfo:RecommendInfo = new RecommendInfo();
				recommedInfo.trackInfo = track;
				recommedInfo.sourceUID = DBAuthenticate.getInstance().dbUID;
				recommedInfo.destUID = destUID;
				var json:Object = JSON.encode(recommedInfo);
				vars.json = json;
				vars.op = "s";
				rst.data = vars;
				op = SEND_RECOMMENDATION;
				rst.method = URLRequestMethod.POST;
				isSendingRequest = true;
				sentReItems.addItem(recommedInfo);
				loader.load(rst);
			}
		}
		
		public function recommendToList(track:TrackInfo,destUIDs:Array):void{
			if(canSend()){
				_curRecommendTrack = track;
				var rst:URLRequest = new URLRequest(encodeURI(Predef.CHAOS_HOST+RECOMMEND_URL));
				var vars:URLVariables = new URLVariables();
				var recommedInfo:RecommendInfo = new RecommendInfo();
				recommedInfo.trackInfo = track;
				recommedInfo.sourceUID = DBAuthenticate.getInstance().dbUID;
				recommedInfo.destUIDs = destUIDs;
				var json:Object = JSON.encode(recommedInfo);
				vars.json = json;
				vars.op = "ss";
				rst.data = vars;
				op = SEND_RECOMMENDATIONS;
				rst.method = URLRequestMethod.POST;
				isSendingRequest = true;
				
				for each( var destID:int in destUIDs){
					recommedInfo = new RecommendInfo();
					recommedInfo.trackInfo = track;
					recommedInfo.sourceUID = DBAuthenticate.getInstance().dbUID;
					recommedInfo.destUID = destID;
					sentReItems.addItem(recommedInfo);
				}
				loader.load(rst);
				
				sendRecommendMail(track,destUIDs);
			}
		}
		
		public function setRecommendLiked(rid:int):void{
			_reOp = "l";
			var reItem:RecommendInfo = getRecommend(rid);
			if(reItem!=null && reItem.source == RecommendInfo.SOURCE_CHAOS){
				responseRecommend(rid,_reOp);
			}else if(reItem.source == RecommendInfo.SOURCE_DOUBANSHUO){
				DBShuoManager.getInstance().likeDBShuoRecommend(rid);
			}
		}
		
		public function setRecommendBaned(rid:int):void{
			_reOp = "b";
			var reItem:RecommendInfo = getRecommend(rid);
			if(reItem!=null && reItem.source == RecommendInfo.SOURCE_CHAOS){
				responseRecommend(rid,_reOp);
			}
			removeRecommendItem(rid);
		}
		
		public function setRecommendPassed(rid:int):void{
			_reOp = "p";
			var reItem:RecommendInfo = getRecommend(rid);
			if(reItem!=null && reItem.source == RecommendInfo.SOURCE_CHAOS){
				responseRecommend(rid,_reOp);
			}
			removeRecommendItem(rid);
		}
		
		public function setTrackInfoInvalid(rid:int):void{
			_reOp = "invalid";
			var reItem:RecommendInfo = getRecommend(rid);
			if(reItem!=null && reItem.source == RecommendInfo.SOURCE_CHAOS){
				responseRecommend(rid,_reOp);
			}else{
				//TODO:
			}
			removeRecommendItem(rid);
		}
		
		public function getRecommendReWithSid(sid:int):void{
			if(canSend()){
				_sid = sid;
				isSendingRequest = true;
				var rst:URLRequest = new URLRequest(encodeURI(Predef.CHAOS_HOST+RECOMMEND_URL));
				var vars:URLVariables = new URLVariables();
				vars.op = "re";
				vars.sid = _sid;
				vars.uid = DBAuthenticate.getInstance().dbUID;
				rst.data = vars;
				loader.load(rst);
			}
		}
		
		public function getRecommendList():ArrayCollection{
			return this.remainList;
		}
		
		public function getContactsRecentLike(list:Array):void{
			if(canSend() && list!=null){
				var rst:URLRequest = new URLRequest(encodeURI(Predef.CHAOS_HOST+RECOMMEND_URL));
				var vars:URLVariables = new URLVariables();
				vars.destIds = list.join("|");
				vars.op = "like";
				vars.uid = DBAuthenticate.getInstance().dbUID;
				rst.data = vars;
				op = GET_CONTACTS_RECENT_LIKE;
				rst.method = URLRequestMethod.GET;
				isSendingRequest = true;
				loader.load(rst);
			}
		}
		
		private function onLogin(e:LoginEvent):void{
			isRegistered = true;
			evtManager.start();
		}
		
		private function onGetNewRes(e:RecommendEvent):void{
			//get the newest douban listen urls for the recommendations
			var originalList:ArrayCollection = e.data as ArrayCollection;
			if(originalList.length>0){
				var reInfo:RecommendInfo;
				var rawTrackList:Array = [];
				this.remainList.addAll(originalList);
				for(var i:int = 0;i<originalList.length;i++){
					reInfo = originalList.getItemAt(i) as RecommendInfo;
					rawTrackList.push(reInfo.trackInfo);
					if(reInfo.trackInfo.ssid==""){
						Logger.getInstance().logInfo("Recommendation track is lack of ssid",reInfo.id,reInfo.sourceUID,reInfo.time,reInfo.trackInfo.sid);
						ChaosLogPanel.getInstance().addBasicInfo("来自"+reInfo.sourceName+"的歌曲"+reInfo.trackInfo.sid+"播放信息不全");
						this.setRecommendPassed(reInfo.id);
					}else{
						Logger.getInstance().logInfo("receive recommendation:",reInfo.trackInfo.name,reInfo.id+" from "+reInfo.sourceUID,reInfo.sourceName,Format.dateFormt(reInfo.time));
					}
				}
				
				var reList:NeedUpdatedPlayList = new NeedUpdatedPlayList(rawTrackList);
				reList.addEventListener(NeedUpdatedPlayList.ALL_ITEMS_LISTEN_READY,onAllReItemsUpdated);
				reList.startUpdate();
				
			}
		}
		
		private function canSend():Boolean{
			return isRegistered==true && isSendingRequest == false
		}
		
		private function onAllReItemsUpdated(e:Event):void{
			var list:NeedUpdatedPlayList = e.currentTarget as NeedUpdatedPlayList;
			list.removeEventListener(NeedUpdatedPlayList.ALL_ITEMS_LISTEN_READY,onAllReItemsUpdated);
			var ev:RecommendEvent = new RecommendEvent(RecommendEvent.GET_NEW_RECOMMENDLIST,
				mergeTrackList(list));
			dispatchEvent(ev);
		}
		
		private function mergeTrackList(rawTrackList:ArrayCollection):ArrayCollection{
			var trackList:ArrayCollection = new ArrayCollection();
			for each(var track:TrackInfo in rawTrackList){
				if(searchTrackInList(track.sid,trackList)==null){
					trackList.addItem(track);
				}
			}
			trackList.source.reverse();
			return trackList;
		}
		
		private function searchTrackInList(sid:int,list:ArrayCollection):TrackInfo{
			if(list!=null){
				for each(var track:TrackInfo in list){
					if(sid==track.sid)
						return track;
				}
				return null;
			}else return null;
		}
		
		private function onHasNewEvents(e:HasNewChaosEvent):void{
			var type:int = e.msgType;
			getEvents(type);
		}
		/**
		 * {err:"ok"}
		 * */
		private function onComplete(e:Event):void{
			var rstStr:String = (e.currentTarget as URLLoader).data as String;
			isSendingRequest = false;
			switch(op){
				case SEND_RECOMMENDATION:
					sentReIds.addItem(parseInt(rstStr));
					(sentReItems.getItemAt(sentReItems.length-1) as RecommendInfo).id = parseInt(rstStr);
					dispatchEvent(new RecommendEvent(RecommendEvent.SEND_A_RECOMMENDATION_SUCCESSFULLY,
						{dest:_destUID,trackName:_curRecommendTrack.name}));
					break;
				case RESPONSE_RECOMMENDATION:
					break;
				case SEND_RECOMMENDATIONS:
					var rids:Array = rstStr.split(",");
					var rinfo:RecommendInfo;
					var idsstr:String = "";
					for (var i:int = 0;i<rids.length;i++){
						rids[i] = (rids[i] as String).replace(/[\[ ]/,'');
						sentReIds.addItem(parseInt(rids[i]));
						rinfo = sentReItems.getItemAt(sentReItems.length-(rids.length-i)) as RecommendInfo;
						rinfo.id = parseInt(rids[i]);
						var destUser:UserInfo = ContactManager.getInstance().getContactInfo(rinfo.destUID);
						if(destUser){
							idsstr+=destUser.dbName+" ";
						}
						else{
							idsstr+=rinfo.destUID+" ";
						}
					}
					dispatchEvent(new RecommendEvent(RecommendEvent.SEND_A_RECOMMENDATION_SUCCESSFULLY,
						{dest:idsstr,trackName:_curRecommendTrack.name}));
					break;
				case GET_EVENT_LIST:
					parseEventList(rstStr);
					break;
				case GET_CONTACTS_RECENT_LIKE:
					parseRecentLikes(rstStr);
					break;
				case GET_RECOMMEND_STAT:
					parseRecommendStat(rstStr);
					break;
			}
		}
		
		//FIX BUG:有的时候pass掉的推荐歌曲还是会播回来。
		private var bufferTimer:Timer;
		private static const BUFFER_MAX:int = 10;
		private function responseRecommend(rid:int=-1,op:String=null):void{
			if(canSend()){
				_curRId = rid;
				var rst:URLRequest = new URLRequest(encodeURI(Predef.CHAOS_HOST+RECOMMEND_URL));
				var vars:URLVariables = new URLVariables();
				if(rid!=-1 && _responeseIDs.indexOf(rid)<0){
					_responeseIDs.push(rid);
					_responsesOps.push(op);
				}
				if(bufferTimer!=null && bufferTimer.running){
					bufferTimer.stop();
				}
				vars.op = _responsesOps.join("|");
				vars.rid = _responeseIDs.join("|");
				rst.data = vars;
				this.op = RESPONSE_RECOMMENDATION;
				rst.method = URLRequestMethod.POST;
				isSendingRequest = true;
				loader.load(rst);
				_responeseIDs = [];
				_responsesOps = [];
				trace("Send Response to Recommend ",rid);
			}else if(isSendingRequest){
				startBufferTimer();
				if(_responeseIDs.length<BUFFER_MAX){
				 if( _responeseIDs.indexOf(rid)<0){
					trace("Recommend Service is busy,put ",rid," into the waiting list");
					_responeseIDs.push(rid);
					_responsesOps.push(op);
				 }else{
					 trace("This Recommend Item ",rid," has entered into the reponse wait list")
				 }
				}else{
					Logger.getInstance().logInfo("Response to recommendations has received max length of wait list at",20);
				}
			}
		}
		
		private function startBufferTimer():void{
			if(bufferTimer==null){
				bufferTimer = new Timer(1000,30);
				bufferTimer.addEventListener(TimeEvent.COMPLETE,onBufferTimeComplete);
			}
			bufferTimer.reset();
			bufferTimer.start();
		}
		private function onBufferTimeComplete(e:TimeEvent):void{
			if(_responeseIDs.length>0){
				trace("向服务器发送剩下的推荐条目回馈");
				responseRecommend();
			}
		}
		
		private function getRecommend(rid:int):RecommendInfo{
			for each(var reItem:RecommendInfo in remainList){
				if(reItem.id == rid){
					return reItem;
				}
			}
			return null;
		}
		
		private function removeRecommendItem(rid:int):void{
			var reItem:RecommendInfo = getRecommend(rid);
			if(reItem!=null){
				var reItemIdx:int = this.remainList.getItemIndex(reItem);
				if(reItemIdx>=0) this.remainList.removeItemAt(reItemIdx);
			}
		}
		
		private var tryTime:int = 0;
		private function onIOError(e:IOErrorEvent):void{
			Logger.getInstance().logError("Recommendation io error when ",op);
			isSendingRequest = false;
			dispatchEvent(new RecommendEvent(RecommendEvent.RECOMMEND_SERVICE_ERR,op));
		}
		
		private function parseEventList(rstStr:String):void{
			var jsonObj:Object = JSON.decode(rstStr);
			switch(jsonObj["type"]){
				case ChaosEvtManager.TYPE_GET_NEW_RECOMMENDS_REPONSES:
 					for each(var evtMsg:Object in (jsonObj.events as Array)){
						var info:ChaosEvtInfo = new ChaosEvtInfo();
						info.content = evtMsg.content as String;
						info.type = ChaosEvtInfo.LIKE_RECOMMEND_TRACK;
						info.time = Format.castStrToDate(evtMsg.time as String);
						dispatchEvent(new RecommendEvent(RecommendEvent.RECEIVER_LIKED,info));
					}
					break;
				case ChaosEvtManager.TYPE_ADDED_AS_DEST_ID:
					onGetAddAsDest(jsonObj.events as Array);
					break;
				case ChaosEvtManager.TYPE_INVITED_UID_ACTTIVATE:
					onInvitedUserActivate(jsonObj.events as Array);
					break;
				case ChaosEvtManager.TYPE_P2PSITE_OPEN:
					onInterestedSiteOpen(jsonObj.events as Array);
					break;
			}
		}
		
		private function parseRecentLikes(rstStr:String):void{
			var rst:Object = JSON.decode(rstStr);
			dispatchEvent(new RecommendEvent(RecommendEvent.GET_CONTACTS_RECENT_LIKES,rst));
		}
		
		private function parseRecommendStat(rstStr:String):void{
			var rst:Object = JSON.decode(rstStr);
			dispatchEvent(new RecommendEvent(RecommendEvent.GET_RECOMMEND_STATICS,rst));
		}
		
		private function onInvitedUserActivate(events:Array):void{
			for each(var evtMsg:Object in events){
				var info:ChaosEvtInfo = new ChaosEvtInfo();
				info.content = evtMsg.content as String;
				info.type = ChaosEvtInfo.INVITED_USER_ACTIVATE;
				info.time = Format.castStrToDate(evtMsg.time as String);
				ChaosLogPanel.getInstance().addInfo(info);
			}
		}
		
		private function onInterestedSiteOpen(events:Array):void{
			for each(var evtMsg:Object in events){
				var info:ChaosEvtInfo = new ChaosEvtInfo();
				info.content = evtMsg.content as String;
				info.type = ChaosEvtInfo.P2P_SITE_STARTED;
				info.time = Format.castStrToDate(evtMsg.time as String);
				if((new Date).time-info.time.time<60000)
					ChaosLogPanel.getInstance().addInfo(info);
				
//				var sites:Array = Format.getEvtP2pSiteInfo(info.content);
//				var site:Object = sites.length>0?sites[0]:null;
//				if(site!=null){
//					P2PSitesManager.getInstance().testSiteOpen(site.id);
//				}
			}
		}
		
		private function onGetAddAsDest(events:Array):void{
			for each(var evtMsg:Object in events){
				var info:ChaosEvtInfo = new ChaosEvtInfo();
				info.content = evtMsg.content as String;
				info.type = ChaosEvtInfo.ADDED_AS_DEST_ID;
				info.time = Format.castStrToDate(evtMsg.time as String);
				ChaosLogPanel.getInstance().addInfo(info);
			}
		}
		
		private function isNeedSendMail(userID:int):Boolean{
			var user:UserInfo = ContactManager.getInstance().getContactInfo(userID);
			if(user!=null){
				if(user.isActivate == UserInfo.ACTIVATE_NO_CHAOS){
					return true;
				}else if(user.isActivate == UserInfo.ACTIVATE_CHAOS){
					var today:Date = new Date();
					if(user.lastActivitTime == null) 
						return true;
					if(today.fullYear == user.lastActivitTime.fullYear){
						if(today.month-user.lastActivitTime.month>=1){
							return true;
						}else{
							return false;
						}
					}else{
						return false;
					}
				}else
					return false;
			}
			else return false;
		}
		
		private function sendRecommendMail(track:TrackInfo,destUIDs:Array):void{
			//给不是Douchaos或者很久没有登录Douchaos用户发豆邮
			for each(var userID:int in destUIDs){
				if(isNeedSendMail(userID)){
					var userInfo:UserInfo = ContactManager.getInstance().getContactInfo(userID);
					if(userInfo!=null){
						mailSender.addSendMail(userID,
							mailSender.getRecommendTitle(userID,userInfo.dbName,track),
							mailSender.getRecommendContent(userID,userInfo.dbName,track));
					}else{
						Logger.getInstance().logError("Err sending DouMail because of lacking Receriver's name when ",userID);
					}
				}
			}
		}
	}
}